﻿using SuperX.Common.AppConfiguration;
using SuperX.Common.Helper;
using log4net;
using log4net.Appender;
using log4net.Core;
using log4net.Repository.Hierarchy;
using Newtonsoft.Json.Linq;
using NLog;
using NLog.Config;
using NLog.Layouts;
using NLog.Targets;
using NLog.Targets.Wrappers;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;

namespace SuperX.Common.Log
{
    public class LoggerBase
    {
        [field: NonSerializedAttribute]
        public event EventHandler<LogEventArgs> LogChanged;

        protected void FireLogChangged(string logLevel, string message)
        {
            var temp = Interlocked.CompareExchange(ref LogChanged, null, null);
            if (temp != null)
            {
                var messages = message.Split(':');
                var realMessage = message;
                if (messages.Length > 3 && DateTime.TryParse(messages[0] + ":" + messages[1] + ":" + messages[2], out var dateTime))
                {
                    realMessage = messages[3].TrimStart(' ');
                }
                else
                {
                    dateTime = DateTime.Now;
                }
                var logSource = Environment.MachineName;
                var e = new LogEventArgs
                {
                    Content = realMessage,
                    LogLevel = logLevel,
                    TriggerTime = dateTime,
                    LogSource = logSource,
                    Type = "SystemLog"
                };
                temp(this, e);
            }
        }

        protected ILog Log4Net { get; set; }

        /// <summary>
        /// Gets or sets the logger.
        /// </summary>
        /// <value>The logger.</value>
        protected NLog.Logger Logger { get; set; }
        /// <summary>
        /// Gets or sets the configuration.
        /// </summary>
        /// <value>The configuration.</value>
        protected LoggingConfiguration Configuration { get; set; }

        public NLog.Logger GetLogger()
        {
            return Logger;
        }

        public void InitNLog(string name, string referencePath)
        {
            try
            {
                if (string.IsNullOrEmpty(referencePath))
                {
                    return;
                }
                InitLog4Net();
                var isSeparateDetailLog = SystemCfgService.GetInstance().SystemCfg.LogConfig.IsSeparateDetailLog;

                var fileTarget = new FileTarget
                {
                    Name = "file",
                    FileName = Path.Combine(referencePath, "${shortdate}.log"),
                    Layout = Layout.FromString(
                            @"${date:format=HH\:mm\:ss} ${logger}|${level:upperCase=true}|${ProcessName}|${ThreadId}|${callsite:skipFrames=2:IncludeNamespace=false}|${message}"),
                    MaxArchiveFiles = SystemCfgService.GetInstance().SystemCfg.LogConfig.LogCacheTime,
                };

                var rules = new List<LoggingRule>();

                var ips = IPUtil.GetLocalIPV4s();
                if (ips == null || ips.Length <= 0)
                {
                    var udpTarget = new CustomNLogViewerTarget
                    {
                        Address = string.Format("UDP://{0}:{1}", "localhost", DefaultPortInfos.LogServicePort),
                        //Address = "UDP://127.0.0.1:9999",
                    };

                    var rule = new LoggingRule { LoggerNamePattern = "*" };
                    var asyncTargetWrapper = new AsyncTargetWrapper(udpTarget);
                    rule.Targets.Add(asyncTargetWrapper);
                    //rule.Targets.Add(fileTarget);
                    rule.EnableLoggingForLevel(LogLevel.Debug);
                    rule.EnableLoggingForLevel(LogLevel.Info);
                    rule.EnableLoggingForLevel(LogLevel.Error);
                    rule.EnableLoggingForLevel(LogLevel.Fatal);
                    rule.EnableLoggingForLevel(LogLevel.Trace);
                    rule.EnableLoggingForLevel(LogLevel.Warn);
                    rules.Add(rule);
                }
                else
                {
                    foreach (var ipAddress in ips)
                    {
                        var udpTarget = new CustomNLogViewerTarget
                        {
                            Address = string.Format("UDP://{0}:{1}", ipAddress.ToString(), DefaultPortInfos.LogServicePort),
                            //Address = "UDP://127.0.0.1:9999",
                        };

                        var rule = new LoggingRule { LoggerNamePattern = "*" };
                        var asyncTargetWrapper = new AsyncTargetWrapper(udpTarget);
                        rule.Targets.Add(asyncTargetWrapper);
                        //rule.Targets.Add(fileTarget);
                        rule.EnableLoggingForLevel(LogLevel.Debug);
                        rule.EnableLoggingForLevel(LogLevel.Info);
                        rule.EnableLoggingForLevel(LogLevel.Error);
                        rule.EnableLoggingForLevel(LogLevel.Fatal);
                        rule.EnableLoggingForLevel(LogLevel.Trace);
                        rule.EnableLoggingForLevel(LogLevel.Warn);
                        rules.Add(rule);
                    }
                }

                var logcfg = SystemCfgService.GetInstance().SystemCfg.LogConfig;


                var rule1 = new LoggingRule { LoggerNamePattern = "*" };
                rule1.Targets.Add(fileTarget);
                rule1.EnableLoggingForLevel(LogLevel.Info);
                rule1.EnableLoggingForLevel(LogLevel.Warn);
                rule1.EnableLoggingForLevel(LogLevel.Error);
                rule1.EnableLoggingForLevel(LogLevel.Fatal);
                if (!isSeparateDetailLog)
                {
                    rule1.EnableLoggingForLevel(LogLevel.Debug);
                    rule1.EnableLoggingForLevel(LogLevel.Trace);
                }

                var config = new LoggingConfiguration();

                foreach (var rule in rules)
                {
                    config.LoggingRules.Add(rule);
                }
                config.LoggingRules.Add(rule1);

                if (isSeparateDetailLog)
                {
                    var fileTarget1 = new FileTarget
                    {
                        Name = "debugFile",
                        FileName = Path.Combine(referencePath, "${shortdate}_detail.log"),
                        Layout = Layout.FromString(
                        @"${date:format=HH\:mm\:ss} ${logger}|${level:upperCase=true}|${ProcessName}|${ThreadId}|${callsite:skipFrames=2:IncludeNamespace=false}|${message}"),
                        MaxArchiveFiles = SystemCfgService.GetInstance().SystemCfg.LogConfig.DetailLogCacheTime,
                    };

                    var rule2 = new LoggingRule { LoggerNamePattern = "*" };
                    rule2.Targets.Add(fileTarget1);
                    rule2.EnableLoggingForLevel(LogLevel.Debug);
                    rule2.EnableLoggingForLevel(LogLevel.Trace);
                    config.LoggingRules.Add(rule2);
                }

                var factory = new LogFactory(config);
                Logger = factory.GetLogger(name);
                Configuration = config;
            }
            catch (Exception ex)
            {
                File.AppendAllText("InternalLog.txt",
                    ex.Message + Environment.NewLine + ex.StackTrace + Environment.NewLine);
            }
        }

        private void InitLog4Net()
        {
            try
            {
                var logConfig = SystemCfgService.GetInstance().SystemCfg.LogConfig;
            }
            catch (Exception)
            {

            }
        }

        /// <summary>
        /// 记录追踪信息
        /// </summary>
        /// <param name="message">追踪信息</param>
        public void Trace(string message)
        {
            try
            {
                FireLogChangged(LogLevel.Trace.ToString(), message);
                Logger.Trace(message);
            }
            catch (Exception ex)
            {
                File.AppendAllText("InternalLog.txt",
                    ex.Message + Environment.NewLine + ex.StackTrace + Environment.NewLine);
            }
        }

        /// <summary>
        /// 记录调试信息
        /// </summary>
        /// <param name="message">调试信息</param>
        public void Debug(string message)
        {
            try
            {
                FireLogChangged(LogLevel.Debug.ToString(), message);
                Logger.Debug(message);
            }
            catch (Exception ex)
            {
                File.AppendAllText("InternalLog.txt", ex.Message + Environment.NewLine + ex.StackTrace + Environment.NewLine);
            }
        }

        /// <summary>
        /// 记录提示信息
        /// </summary>
        /// <param name="message">提示信息</param>
        public void Info(string message)
        {
            try
            {
                FireLogChangged(LogLevel.Info.ToString(), message);
                Logger.Info(message);
                Log4Net?.Info(message);
            }
            catch (Exception ex)
            {
                File.AppendAllText("InternalLog.txt", ex.Message + Environment.NewLine + ex.StackTrace + Environment.NewLine);
            }
        }

        /// <summary>
        /// 记录警告信息
        /// </summary>
        /// <param name="message">警告信息</param>
        public void Warn(string message)
        {
            try
            {
                FireLogChangged(LogLevel.Warn.ToString(), message);
                Logger.Warn(message);
                Log4Net?.Warn(message);
            }
            catch (Exception ex)
            {
                File.AppendAllText("InternalLog.txt", ex.Message + Environment.NewLine + ex.StackTrace + Environment.NewLine);
            }
        }

        /// <summary>
        /// 记录错误信息
        /// </summary>
        /// <param name="message">错误信息</param>
        public void Error(string message)
        {
            try
            {
                FireLogChangged(LogLevel.Error.ToString(), message);
                Logger.Error(message);
                Log4Net?.Error(message);
            }
            catch (Exception ex)
            {
                File.AppendAllText("InternalLog.txt", ex.Message + Environment.NewLine + ex.StackTrace + Environment.NewLine);
            }
        }

        /// <summary>
        /// 记录致命错误
        /// </summary>
        /// <param name="message">致命错误</param>
        public void Fatal(string message)
        {
            try
            {
                FireLogChangged(LogLevel.Fatal.ToString(), message);
                Logger.Fatal(message);
                Log4Net?.Fatal(message);
            }
            catch (Exception ex)
            {
                File.AppendAllText("InternalLog.txt", ex.Message + Environment.NewLine + ex.StackTrace + Environment.NewLine);
            }
        }

        public void WriteLogService(JObject log)
        {
            try
            {
                var logUrl = SystemCfgService.GetInstance().SystemCfg.LogConfig.WebApipushUrl;
                var url = (HttpWebRequest)WebRequest.Create(string.Format(logUrl));

                HttpWebRequest wReq = url;
                wReq.Method = "Post";
                wReq.ContentType = "application/json";
                wReq.Timeout = 5000;
                StringBuilder builder = new StringBuilder();
                var requsetdata = JsonHelper.JsonSerialize(log);

                byte[] data = Encoding.UTF8.GetBytes(requsetdata);
                wReq.ContentLength = data.Length;
                Stream reqStream = wReq.GetRequestStream();
                reqStream.Write(data, 0, data.Length);
                reqStream.Close();
                string result;
                using (StreamReader sr = new StreamReader(wReq.GetResponse().GetResponseStream()))
                {
                    result = sr.ReadToEnd();
                }
            }
            catch (Exception ex)
            {
                Logger.Warn($"CommonLog error {ex.Message}");
            }
        }


        protected void Close()
        {
            if (Configuration == null)
                return;
            try
            {
                foreach (var target in Configuration.AllTargets)
                {
                    target.Dispose();
                }
            }
            catch (Exception)
            {
            }
            Configuration = null;
        }
    }
}
